/*
 * Copyright (C) 2017 Intel Corporation.  All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */


// Copyright (c) WanSheng Intelligent Corp. All rights reserved.

#ifndef APPS_IAGENT_CORE_LIB_AGENT_CORE_LIB_H_
#define APPS_IAGENT_CORE_LIB_AGENT_CORE_LIB_H_

#include <inttypes.h>
#include "iagent_bsp.h"
#include "misc.h"
#include "logs.h"
#include "hw_serials_num.h"


#ifdef __cplusplus
extern "C" {
#endif

#define S_PENDING_DELETE  1

#define COUNT_OF(x)  (sizeof(x)/sizeof(x[0]))



/************************************************************************/
/*                                                                      */
/*                           message_queue.c                              */
/*                                                                      */
/************************************************************************/

typedef void (*bh_msg_handler) (void *msg);
typedef void (*bh_msg_cleanup_f) (void * msg_body);
typedef uint32_t msg_tag_t;
typedef struct _msg_t * msg_t;
typedef struct _msg_queue_t * msg_queue_t;

#define MSG_TAG_NONE 0


//message_queue
msg_queue_t create_queue();
void release_queue(msg_queue_t  queue);
msg_queue_t create_queue2(unsigned int max_queue_size);

msg_tag_t get_msg_tag(msg_t msg);

void * get_msg_body(msg_t msg);

uint32_t get_msg_buffer_len(msg_t msg);

uint32_t get_msg_queue_drops(msg_queue_t queue);


// @param msg:
// 1. will be freed automatically if call returned failed.
// 2. reciver must free the msg explicitly
// 3. caller must not access the msg after the call.
// @return: fail if the max queue is reached.
bool post_msg(msg_queue_t queue, msg_t msg);

// post a buffer with length, which must be allocated from heap
// the buffer will be reset to NULL after the call, no matter return success or fail.
bool post_msg_buffer(msg_queue_t queue, msg_tag_t tag, char **buffer, uint32_t len);


// post a buffer with length.
// the user buffer will be duplicated for sending. 
bool post_msg_dup(msg_queue_t queue, msg_tag_t tag, char *buffer, unsigned int len);

// body:
// handed over to the returned msg, 
// it will be freed by body_cleaner when in the free_msg().
// the pointer will be reset to NULL after the call.
msg_t new_msg( void **body, msg_tag_t tag, bh_msg_cleanup_f body_cleaner);

void free_msg(msg_t msg);

msg_t get_msg(msg_queue_t queue, int timeout);
msg_t get_msg_call_handler(msg_queue_t queue, int timeout);
msg_t get_msg_timeout_ms(msg_queue_t queue, int timeout_ms);
msg_t get_msg_timeout_us(msg_queue_t queue, uint64_t timeout_us);



/*******************************************************
 * 
 *       transaction API
 *
 *******************************************************/
typedef enum{
	T_Empty,
	T_Raw,
	T_Coap_Raw,
	T_Coap_Parsed,              // coap_packet_t
	T_iLink_Parsed,             // ilink_message_t
	T_Broker_Message_Handle,    // MODULE_HANDLE
	T_IDRM_Response,            // restful_response_t
    T_Plain_Text,
    T_JSON_Text,

	T_Trans_User_Fmt = 100
}transaction_resp_fmt_t;

typedef enum {
    T_Undefined_Sync = 0,
    T_Sync_Trans = 1,
    T_Async_Trans = 2
} transaction_type_t;

typedef struct sync_ctx * sync_ctx_t;

typedef int (*bh_async_callback) (void * user_data, void * response, int len, transaction_resp_fmt_t format);

typedef uint32_t transaction_id_t;

//#define CTX_BUFFER_CECK 1
#ifdef CTX_BUFFER_CECK
void * trans_malloc_ctx(int len);
void   trans_free_ctx(void *);
#else
#define trans_malloc_ctx malloc
#define trans_free_ctx free
#endif

#define TRANSACTION_ID_INVALID  (uint32_t)(-1)
#define TRANSACTION_ID_AUTO     (uint32_t)(-1)


sync_ctx_t  create_sync_ctx();

void delete_sync_ctx(sync_ctx_t ctx);

uint32_t bh_get_next_expiry_ms(sync_ctx_t sync_ctx);


/*
  @brief:  blocking wait the response for the transaction with specified ID.
           The response is feeded from another thread by calling bh_feed_response().
  @param   len: [out] response length
  @return: the response data pointer. The caller must take care of data freeing.  
           NULL - wait timeout.
*/
void* bh_wait_response(sync_ctx_t sync_ctx, transaction_id_t id, int * len, int * fmt, uint32_t timeout_ms);


/*  @brief:      Feed the response to the party who is waiting on this transaction:
*              - For asynchronized wait, response will be cusumed by callback in the same thread. 
 *               and the callback should not free it.
 *             - For synchronized wait, the response will be copied as buffer when resp_buffer_size is not zero.
 *               If resp_buffer_size is zero, the caller and callback use their own contract for freeing the response.
 *
 *  @param: resp_buffer_size - None zero value only when the response is a memory buffer that can be duplicated.
 *                             Set it to ZERO_HANDLE_LENGTH for HANDLE or stucture pointer containing sub pointers
*/
bool bh_feed_response(sync_ctx_t sync_ctx, transaction_id_t id, void * response,  uint32_t resp_buffer_size, transaction_resp_fmt_t format);
#define ZERO_HANDLE_LENGTH 0


/*
 *  @brief:    handover the response data to the transaction.
 *             if it succeeds, the value of reponse will be reset to NULL.
*/
bool bh_transaction_handover_response(sync_ctx_t sync_ctx, transaction_id_t id, void ** response, uint8_t format);

// id: TRANSACTION_ID_AUTO - ID is automatically generated and returned.
transaction_id_t bh_wait_response_async(sync_ctx_t  sync_ctx, transaction_id_t id, bh_async_callback callback, 
    void* ctx_data, uint32_t timeout, void * worker_thread);

transaction_id_t bh_transaction_new(sync_ctx_t sync_ctx, 
        bh_async_callback cb, void* ctx_data, uint32_t timeout_ms);

transaction_id_t bh_gen_id(sync_ctx_t  ctx);

uint32_t bh_handle_expired_trans(sync_ctx_t sync_ctx);

sync_ctx_t get_outgoing_requests_ctx();



/************************************************************************/
/*                                                                      */
/*                           task.c                                     */
/*                                                                      */
/************************************************************************/
typedef struct _bh_task  * wa_task_t;
typedef struct _task_ctx * wa_task_scheduler_t;

typedef int (*bh_task_handler) (wa_task_t task);
typedef int (*bh_task_close_handler) (wa_task_t task);

struct _bh_task
{
	struct _bh_task * next;
	char* task_type;
	char * task_data;
	bh_task_handler handler;
	bh_task_close_handler close_handler;
	int repeat_interval_msecs;  // 0 - no repeat, delete it after execution
	tick_time_t next_execution;
	unsigned int exec_count;
    bool task_data_is_allocated;
};

typedef struct _bh_task_stats
{
    int g_total_tasks;
}bh_task_stats_t;

wa_task_scheduler_t bh_init_task_scheduler();
wa_task_t bh_new_task(char* task_name, void * data, bool auto_free_data, int repeat_duration_ms, bh_task_handler task_handler);
void bh_delete_task(wa_task_t task);
wa_task_t bh_schedule_task(wa_task_scheduler_t task_ctx,wa_task_t task, int msecs_from_now);
int bh_execute_task(wa_task_scheduler_t task_ctx,wa_task_t task);
void bh_wait_for_task(wa_task_scheduler_t task_ctx,int milli_seconds);
wa_task_t bh_remove_task_by_data(wa_task_scheduler_t task_ctx,void * data_ptr);
wa_task_t bh_remove_task_by_name(wa_task_scheduler_t task_ctx, char * name);
bool bh_reschedule_task(wa_task_scheduler_t task_ctx, char * task_name, int ms);
int bh_remove_all_tasks_by_name(wa_task_scheduler_t task_ctx, char * name);
void bh_get_task_stats(wa_task_scheduler_t task_ctx,bh_task_stats_t * stats);
void bh_dump_tasks(wa_task_scheduler_t task_ctx, FILE * fp, const char * tag);
wa_task_t bh_check_task(wa_task_scheduler_t task_ctx, int * msecs_to_first_task);
int task_check_expiry_ms(wa_task_scheduler_t task_ctx );
void bh_task_destroy_scheduler(wa_task_scheduler_t task_ctx);
wa_task_scheduler_t bh_task_run_new_scheduler();

/************************************************************************/
/*                                                                      */
/*                           url_match.c                              */
/*                                                                      */
/************************************************************************/

extern int check_url_start(const char* url, int url_len, const char * leading_str);
bool match_url(char * pattern, char * matched);


/************************************************************************/
/*                                                                      */
/*                           path_util.c                              */
/*                                                                      */
/************************************************************************/
char * getExecPath (char * path,size_t dest_len, char * argv0);
char * get_module_path(void* address);
char * make_upper_path(char * path, int levels);
char * generate_upper_path_A(char * path, int levels);


#ifdef __cplusplus
}
#endif

#endif /* APPS_IAGENT_CORE_LIB_AGENT_CORE_LIB_H_ */
